package com.example.ormsqlsplicingframe1.warpper;

import com.baomidou.mybatisplus.annotation.TableField;
import com.example.ormsqlsplicingframe1.note.*;
import com.example.ormsqlsplicingframe1.orm.Util.StringUtil;
import com.example.ormsqlsplicingframe1.orm.note.Title;
import com.example.ormsqlsplicingframe1.orm.note.Where;
import com.example.ormsqlsplicingframe1.session.SessionUtil;
import com.example.ormsqlsplicingframe1.sql.ConditionSql;
import com.example.ormsqlsplicingframe1.sql.Sql;
import com.example.ormsqlsplicingframe1.sql.impl.SelectImpl;
import com.example.ormsqlsplicingframe1.utlis.GetGenerics;
import com.example.ormsqlsplicingframe1.utlis.ObjValue;
import com.example.ormsqlsplicingframe1.utlis.Page;
import lombok.SneakyThrows;


import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

public class SelectSqlWrapper<E> implements Sql {


    private SelectImpl select = new SelectImpl();

    private static volatile  SelectSqlWrapper selectSqlWrapper;



    private Map<String, Field> map = new HashMap<>();

    private Class<E> aClass;

    private Object object;



    public List<E> selectList(){
        List<E> es = SessionUtil.getSession().select(this.select, aClass);
        return es;
    }

    public SelectSqlWrapper<E> toggleData(String name){
         SessionUtil.getSession().toggleData(name);
       return this;
    }

    public E selectOnt(){

        E es = SessionUtil.getSession().selectOnt(this.select, aClass);
        return es;
    }




    @Override
    public List<Object> getValues() {
        return select.getValues();
    }

    private ConditionSql condition(){
     return select.where();
    }


    public SelectSqlWrapper<E> where(Consumer<ConditionSql> consumer) {
        consumer.accept(condition());
        return this;
    }

    public Page<E> page(){

        Page page = new Page();

        page.setList(selectList());
        page.setTotal(Long.hashCode(SessionUtil.getSession().count(select)));

        return page;
    }

    private void wrapper(){
        wrapper(aClass);
    }

    private void wrapper(Class<E> aClass){

         Title annotation = aClass.getAnnotation(Title.class);

        if (annotation == null){
          throw new RuntimeException("当前没有添加 Title 注解");
        }
        select.table(annotation.titleName());

        if (annotation.leftJoinList() != null){
            String[] strings = annotation.leftJoinList();
            for (String s : strings) {
                int on = s.indexOf("on");
                String substring1 = s.substring(0,on);
                String substring = s.substring(on+2);
                select.left(substring1,f -> f.sql(substring));
            }

        }

        if (annotation.rightJoinList() != null){
            String[] strings = annotation.rightJoinList();
            for (String s : strings) {
                int on = s.indexOf("on");
                String substring1 = s.substring(on);
                String substring = s.substring(on + 2);
                select.right(substring1,f -> f.sql(substring));
            }
        }

        //获取po对象的字段
        getObjField();

        JoinLimit limit = aClass.getAnnotation(JoinLimit.class);
        if (limit != null){

            select.page(limit.pageIndex(),limit.pageSize());
        }

        if (aClass.getSuperclass() != Object.class){
            reflect(aClass);
            wrapper((Class<E>) aClass.getSuperclass());
        }else {
            reflect(aClass);
        }


    }

    private void reflect(Class<?> aClass){
        for (Field field : aClass.getDeclaredFields()) {

            if (field.getAnnotation(JoinFieldMajorKey.class)!= null){

                select.fields(f ->{
                    String column = "";

                    column = field.getName();

                    f.field(StringUtil.javaTurnSql(column),field.getName());
                });

            }

            TableField tableField = field.getAnnotation(TableField.class);
            if ( tableField != null && tableField.exist()){

                select.fields(f ->{
                    String column = "";
                    if (!tableField.value().equals("")){
                        column = tableField.value();
                    }else {
                        column = field.getName();
                    }
                    f.field(StringUtil.javaTurnSql(column),field.getName());
                });

                JoinWhere where = field.getAnnotation(JoinWhere.class);
                if (where != null){
                    where(where,tableField);
                }
            }


        }
    }


    private void getObjField(){
        if (ObjValue.isNotNull(object)){
            map=Arrays.stream(object.getClass().getDeclaredFields()).sequential().collect(Collectors.toMap(field -> field.getName(), field -> field));
        }
    }

    @SneakyThrows
    private void where(JoinWhere where, TableField tableField ){


        Field field = map.get(where.value());
        if (field != null){
            field.setAccessible(true);
            Object value = field.get(object);
            if (ObjValue.isNotNull(value)){

                select.where(eq -> {
                    if (where.condition() == ConditionEnum.eq){
                      if (where.and()){
                          eq.eq(returnValue(where,tableField),value);
                      }else {
                          eq.or(or ->{
                              or.eq(  returnValue(where,tableField),value);
                          });
                      }
                    }
                    if (where.condition() == ConditionEnum.likeRight){
                        if (where.and()){
                            eq.rightLike(returnValue(where,tableField),value);
                        }else {
                            eq.or(or ->{
                                or.rightLike(  returnValue(where,tableField),value);
                            });
                        }
                    }
                    if (where.condition() == ConditionEnum.likeLeft){
                        if (where.and()){
                            eq.leftLike(returnValue(where,tableField),value);
                        }else {
                            eq.or(or ->{
                                or.leftLike(  returnValue(where,tableField),value);
                            });
                        }
                    }
                });
            }
        }



    }

    //返回对应的sql
    private String returnValue(JoinWhere where, TableField tableField){

        String name =null;

        if (ObjValue.isNotNull(where.customization())){
            name = where.customization();
        }else {
            name = tableField.value();
        }

        return name;
    }

    @SneakyThrows
    private void where(Where where, TableField tableField ){


           Field field = map.get(where.substitution());
           if (field != null){
               field.setAccessible(true);
               Object value = field.get(object);
               if (ObjValue.isNotNull(value)){

                   select.where(eq -> {
                       String name = "";
                       if (ObjValue.isNotNull(where.customization())){
                           name = where.customization();
                       }else {
                           name = where.eq();
                       }
                       if (ObjValue.isNotNull(where.eq())){
                           eq.eq(name,value);
                       }
                       if (ObjValue.isNotNull(where.leftLike())){
                           eq.leftLike(name,value);
                       }

                       if (ObjValue.isNotNull(where.rightLike())){
                           eq.rightLike(name,value);
                       }
                   });
               }
           }



    }

    public String getSql(){

        return select.getSql();
    }

    public  static <E> SelectSqlWrapper<E> newSelectSqlWrapper(Class<E> aClass, Object object){

        if (selectSqlWrapper == null){
            synchronized (selectSqlWrapper){
                if (selectSqlWrapper == null){
                    selectSqlWrapper = new SelectSqlWrapper(aClass);
                }
            }

        }

        selectSqlWrapper.setObject(object).setaClass(aClass);


        return selectSqlWrapper;


    }



    public synchronized static <E> SelectSqlWrapper<E> newSelectSqlWrapper(Class<E> aClass){

//        if (selectSqlWrapper == null){
//
//                if (selectSqlWrapper == null){
//                    selectSqlWrapper = new SelectSqlWrapper(aClass);
//                }
//
//            }
//
//        selectSqlWrapper.setObject(aClass);


        SelectSqlWrapper<E> wrapper = new SelectSqlWrapper<>(aClass);

        return wrapper;

    }

    private SelectSqlWrapper<E> setaClass(Class<E> aClass) {
        this.aClass = aClass;
        return this;
    }

    private SelectSqlWrapper<E> setObject(Object object) {
        this.object = object;

        return this;
    }

    public SelectSqlWrapper() {
    }

    public SelectSqlWrapper(Class<E> aClass, Object object) {
        this.aClass = aClass;
        this.object = object;

        wrapper();
    }

    public SelectSqlWrapper(Class<E> aClass) {
        this.aClass = aClass;
        wrapper();
    }
}
